﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using System.Threading;

namespace SerialManager
{
    public enum FileListBuilderStatus
    {
        InstanceCreated,
        BuildingStarted,
        BuildingFinished,
        PreparingMovement,
        MovementPrepared
    }

    /// <summary>
    /// Trida pro sestaveni seznamu souboru patricich serialu
    /// </summary>
    class FileListBuilder
    {
        /// <summary>
        /// Indikator, zda sestavovani seznamu skoncilo
        /// </summary>
        public FileListBuilderStatus Status { get; private set; }

        /// <summary>
        /// Vstupni seznam souboru, ktery se zpracovava
        /// Nastavene v konstruktoru
        /// </summary>
        public readonly FileInfo[] InputFileList;

        /// <summary>
        /// Seznam nalezenych souboru
        /// Naplnene metodou BuildFilesList
        /// </summary>
        public ArrayList FoundFiles { get; private set; }

        /// <summary>
        /// Soubory, ktere nebyly rozpoznany jako video nebo titulek 
        /// Naplnene metodou BuildFilesList
        /// </summary>
        public FileInfo[] UnknownFiles { get; private set; }

        /// <summary>
        /// V nazvu techto souboru nebyly rozpoznany informace o sezone a epizode
        /// Naplnene metodou BuildFilesList
        /// </summary>
        public FileInfo[] NoMatchFiles { get; private set; }

        /// <summary>
        /// Evidence prejmenovani souboru
        /// Naplnene metodou PrepareMove
        /// </summary>
        public MoveResult FilesForMove { get; private set; }

        /// <summary>
        /// Vytvori a spusti thread, ktery sestavuje soubory ke zpracovani
        /// </summary>
        /// <param name="serialDirectory">Hlavní adresář seriálu</param>
        /// <param name="thread">Vytvořený thread</param>
        /// <returns>Instance teto tridy</returns>
        public static FileListBuilder CreateAndStartThread( FileInfo[] files, out Thread thread )
        {
            var builder = new FileListBuilder( files );
            thread = new Thread( new ThreadStart( builder.BuildFilesList ) );
            thread.Start();
            return builder;
        }

        /// <summary>
        /// Konstruktor tridy
        /// </summary>
        /// <param name="files">Seznam souboru, ktere se maji zpracovat</param>
        public FileListBuilder( FileInfo[] files )
        {
            InputFileList = files;
        }

        /// <summary>
        /// Metoda threadu - sestaveni seznamu souboru
        /// </summary>
        public void BuildFilesList()
        {
            Status = FileListBuilderStatus.BuildingStarted;

            // zakladni vyhledani platnych souboru podle pripony videa nebo titulku
            var extensions = Core.VideoExtensions.Concat( Core.SubtitleExtensions ).ToArray();
            var validFiles = (
                from file in InputFileList
                where file.HasAnyExtension( extensions )
                select file
            );

            // soubory s neznamou priponou pripravime bokem
            UnknownFiles = (
                from file in InputFileList.Except( validFiles )
                where ( file.Attributes & FileAttributes.Archive ) != 0
                select file
            ).ToArray();

            // parsujeme jednotlive nazvy souboru a hledame informace o sezone a epizode
            var matches = (
                from file in validFiles
                select new
                {
                    File = file,
                    Info = EpisodeInfo.SearchForMatch( file )
                }
            );

            var noMatch = new List<FileInfo>();

            // konecne zparovani souboru, ktere k sobe patri (videa/titulky)
            foreach( var match in matches )
            {
                // mezi nezname soubory pridame ty, co neodpovidaji zadne masce cisla epizody
                if( match.Info == null )
                {
                    noMatch.Add( match.File );
                    continue;
                }

                var index = FoundFiles.IndexOf( match.Info );

                // k teto epizode jeste nebyly nalezeny zadne soubory, vytvorit prazdny zaznam
                if( index == -1 )
                {
                    var newFiles = new EpisodeFiles( match.Info );
                    newFiles.Add( match.File );
                    FoundFiles.Add( newFiles );
                }
                else
                    ( (EpisodeFiles) FoundFiles[ index ] ).Add( match.File );
            }

            NoMatchFiles = noMatch.ToArray();

            Status = FileListBuilderStatus.BuildingFinished;
        }

        /// <summary>
        /// Priprava na prejmenovani souboru
        /// </summary>
        /// <param name="episodeTitles">Pole informaci o epizodach obsahujici nazvy epizod</param>
        public void PrepareMove( List<EpisodeInfo> episodeTitles )
        {
            if( Status <= FileListBuilderStatus.BuildingStarted )
                return;

            if( FoundFiles.Count == 0 )
            {
                Status = FileListBuilderStatus.MovementPrepared;
                return;
            }

            Status = FileListBuilderStatus.PreparingMovement;

            var forMove = new MoveResult();

            bool lookForTitle = ( episodeTitles != null && episodeTitles.Count > 0 );
            foreach( EpisodeFiles episode in FoundFiles )
            {
                if( lookForTitle )
                {
                    var index = episodeTitles.IndexOf( episode.Info );
                    if( index >= 0 )
                        episode.Info.Title = episodeTitles[ index ].Title;
                }
                episode.PrepareMove( ref forMove );
            }

            FilesForMove = forMove;
            Status = FileListBuilderStatus.MovementPrepared;
        }

        /// <summary>
        /// Provede konecne prejmenovani souboru
        /// </summary>
        public void ProcessMove()
        {
            foreach( var moveInfo in FilesForMove )
                moveInfo.Key.MoveTo( moveInfo.Value.FullName );
        }

        /// <summary>
        /// Trida pro evidenci informaci o chystanem prejmenovani souboru
        /// Klicem je originalni soubor a hodnotou nova absolutni cesta
        /// </summary>
        public class MoveResult : Dictionary<FileInfo, FileInfo>
        {
            public void Add( FileInfo original, string targetPath )
            {
                var newName = targetPath + original.Extension;
                if( newName == original.FullName )
                    return;
                base.Add( original, new FileInfo( newName ) );
            }
        }
    }
}
